package com.mjk.common.web.getway.restful.filter;

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.poi.util.IOUtils;


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

public class HttpRequest extends HttpServletRequestWrapper {
	private String body;
    
    private ServletInputStream inputStream;
    
    private BufferedReader reader;
    
    private String requestURI;
    
    private StringBuilder requestURL;
    
    private String servletPath;
    
    private Map<String, ?> params;
    
    public HttpRequest(HttpServletRequest request) throws IOException {
        super(request);
        if (!isMultipart()) {
            preLoadBody(request);
        }
    }

    private void preLoadBody(HttpServletRequest request) throws IOException { 	
    	Charset charset = Charset.forName(getCharacterEncoding());
        byte[] bodyBytes = IOUtils.toByteArray(request.getInputStream());
        body = new String(bodyBytes, charset);
        inputStream = new RequestCachingInputStream(body.getBytes());
        requestURI = request.getRequestURI();
    }
    
    public final boolean isMultipart() {
        String contentType = getContentType();
        return contentType != null && contentType.toLowerCase().startsWith("multipart/");
    }
    
    @Override
    public final String getContentType() {
        String contentType = getParameter("_contentType");
        if (contentType != null) return contentType;
        return super.getContentType();
    }

    public String getBody() {
        if (isMultipart()) throw new IllegalStateException("multipart request does not support preloaded body");
        return body;
    }

    @Override
    public String getMethod() {
        String method = getParameter("_method");
        if (method != null) return method;
        return super.getMethod();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) return inputStream;
        return super.getInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }
    
    @Override
    public final String getCharacterEncoding() {
        String defaultEncoding = super.getCharacterEncoding();
        return defaultEncoding != null ? defaultEncoding : "UTF-8";
    }
    
    public void setParams(Map<String, ?> params) {
        this.params = params;
    }
    /**
     * used to cache the request inputstream
     * @author sylorl
     * @date Mar 10, 2015
     */
    private static class RequestCachingInputStream extends ServletInputStream {
        
        private final ByteArrayInputStream inputStream;

        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }

        @Override
        public int read() {
            return inputStream.read();
        }

		@Override
		public boolean isFinished() {
			return false;
		}

		@Override
		public boolean isReady() {			
			return false;
		}

		@Override
		public void setReadListener(ReadListener listener) {
			//do noting
		}

		
    }



    @Override
    public StringBuffer getRequestURL() {
        if(this.requestURL == null) {
            return super.getRequestURL();
        }
        return new StringBuffer(this.requestURL.toString());
    }

    @Override
    public String getServletPath() {
        if(servletPath == null) {
            return super.getServletPath();
        }
        return this.servletPath;
    }

    public void setRequestURI(String requestURI, HttpServletRequest request) {
        this.servletPath = requestURI;
        this.requestURI = request.getContextPath() + requestURI;
        this.requestURL = new StringBuilder().append(request.getProtocol())
                                            .append("://")
                                            .append(request.getLocalAddr())
                                            .append("/")
                                            .append(servletPath);
    }
    
    @Override
    public String[] getParameterValues(String name) {
        if(params != null) {
            Object v = params.get(name);
            if (v==null) {
                return new String[]{};
            } else if(v instanceof String[] arr) {
                return arr;
            } else if(v instanceof String str) {
                return new String[]{str};
            } else {
                return new String[]{v.toString()};
            }
        }
        return super.getParameterValues(name);
    }
    @Override
    public String getParameter(String name) {
        if(params != null) {
            Object v = params.get(name);
            if(v == null) {
                return null;
            } else if(v instanceof String[] strArr) {
                if(strArr.length > 0){
                    return strArr[0];
                } else {
                    return null;
                }
            } else if(v instanceof String str) {
                return str;
            } else {
                return v.toString();
            }
        } 
        return super.getParameter(name);
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        if(params != null){
            Map<String, String[]> map = new HashMap<>();
            for(String key:params.keySet()){
                 Object v = params.get(key);
                 String[] strArr;
                 if(v == null){
                     return map;
                 } else if(v instanceof String[] arr) {
                     strArr = arr;
                 } else if(v instanceof String str) {
                     strArr = new String[1];
                     strArr[0] = str;
                 } else {
                     strArr = new String[1];
                     strArr[0] = v.toString();
                 }
                 map.put(key, strArr);
            }
            return map;
        }
        return super.getParameterMap();
    }

}
